package com.wei.czz.framework.index.manager;

import com.alibaba.fastjson2.JSON;
import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.dto.index.login.LoginDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.admin.user.UserContactFlowEnum;
import com.wei.czz.common.enums.admin.user.LoginTypeEnum;
import com.wei.czz.common.enums.admin.user.RequestChannelEnum;
import com.wei.czz.common.enums.admin.userRole.RelationModeEnum;
import com.wei.czz.common.enums.common.dict.DictEnum;
import com.wei.czz.common.enums.index.validateCode.ValidateCodeTypeEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.property.AccessTokenProperty;
import com.wei.czz.common.utils.CaesarUtils;
import com.wei.czz.common.utils.ContextUtils;
import com.wei.czz.common.utils.RandomNumUtils;
import com.wei.czz.common.utils.ShaUtils;
import com.wei.czz.common.vo.index.sign.RegisterVo;
import com.wei.czz.framework.admin.entity.*;
import com.wei.czz.framework.admin.helper.UserHelper;
import com.wei.czz.framework.admin.service.*;
import com.wei.czz.framework.admin.service.impl.DeptUserServiceImpl;
import com.wei.czz.framework.admin.service.impl.UserLoginFlowServiceImpl;
import com.wei.czz.framework.admin.service.impl.UserLoginServiceImpl;
import com.wei.czz.framework.common.entity.IpAddressEntity;
import com.wei.czz.framework.common.service.*;
import com.wei.czz.framework.common.wrapper.DictWrapper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-12-07 21:03:02
 * className: UserRegisterManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class UserRegisterManager {

    private static final Logger log = LoggerFactory.getLogger(UserRegisterManager.class);

    private final AccessTokenProperty accessTokenProperty;

    private final UserService userService;

    private final UserRoleService userRoleService;

    private final UserContactFlowService userContactFlowService;

    private final DeptUserServiceImpl deptUserService;

    private final UserLoginServiceImpl userLoginService;

    private final UserAccessTokenService userAccessTokenService;

    private final UserLoginFlowServiceImpl userLoginFlowService;

    private final UserSequenceService userSequenceService;

    private final ValidateCodeService validateCodeService;

    private final IpAddressService ipAddressService;

    private final AsyncService asyncService;

    private final AesService aesService;

    private final UserHelper userHelper;

    private final DictWrapper dictWrapper;

    public LoginDto registerUser(RegisterVo registerVo) {
        // 获取用户请求的ip地址
        String loginIP = ContextUtils.get(Constant.REQUEST_IP);

        long now = System.currentTimeMillis();
        Date date = new Date(now);

        if (StringUtils.isNotBlank(registerVo.getPhone())) {
            // 手机号加密
            registerVo.setPhone(aesService.encryptToHex(registerVo.getPhone()));
        }
        if (StringUtils.isNotBlank(registerVo.getEmail())) {
            // 邮箱号加密
            registerVo.setEmail(aesService.encryptToHex(registerVo.getEmail()));
        }

        // 异步执行
        CompletableFuture<CzzException> phoneFuture = asyncService.supplyAsync(() -> {
            CzzException czzException = null;
            try {
                if (StringUtils.isNotBlank(registerVo.getPhone())) {
                    // 校验短信验证码
                    validateCodeService.checkValidateCode(registerVo.getPhone(), loginIP,
                            ValidateCodeTypeEnum.REGISTER_USER, registerVo.getPhoneCode());
                }
            } catch (CzzException e) {
                czzException = e;
            } catch (Exception e) {
                log.error("异步校验短信验证码异常。message={}", e.getMessage(), e);
                czzException = new CzzException(ResultEnum.SERVICE_FAIL);
            }
            return czzException;
        });
        // 异步执行
        CompletableFuture<CzzException> emailFuture = asyncService.supplyAsync(() -> {
            CzzException czzException = null;
            try {
                if (StringUtils.isNotBlank(registerVo.getEmail())) {
                    // 校验邮件验证码
                    validateCodeService.checkValidateCode(registerVo.getEmail(), loginIP,
                            ValidateCodeTypeEnum.REGISTER_USER, registerVo.getEmailCode());
                }
            } catch (CzzException e) {
                czzException = e;
            } catch (Exception e) {
                log.error("异步校验邮箱验证码异常。message={}", e.getMessage(), e);
                czzException = new CzzException(ResultEnum.SERVICE_FAIL);
            }
            return czzException;
        });
        try {
            List<CzzException> list = new ArrayList<>(3);
            list.add(phoneFuture.get());
            list.add(emailFuture.get());
            for (CzzException czzException : list) {
                if (czzException != null) {
                    throw czzException;
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            log.error("异步校验用户注册内容异常。message={}", e.getMessage(), e);
            throw new CzzException(ResultEnum.SERVICE_FAIL);
        }

        /*
            判断联系方式关联用户是否存在
         */
        userService.checkLoginInfo(null, registerVo.getPhone(), registerVo.getEmail(), null);

        // 获取用户默认所属部门
        long deptId;
        try {
            String sDeptId = dictWrapper.getOneDict(DictEnum.DEFAULT_USER_DEPT).getValue();
            log.error("字典配置用户默认所属部门主键={}", sDeptId);
            // 设置参数值
            deptId = Long.parseLong(sDeptId);
        } catch (Exception e) {
            log.error("字典配置用户默认所属部门主键有误。message={}", e.getMessage(), e);
            throw new CzzException("字典配置的部门主键有误，请务必及时处理");
        }

        // 获取用户默认角色
        List<Long> roleIdList;
        try {
            String value = dictWrapper.getOneDict(DictEnum.DEFAULT_USER_ROLE).getValue();
            log.error("字典配置用户默认关联角色主键列表={}", value);
            roleIdList = JSON.parseArray(value, Long.class);
        } catch (Exception e) {
            log.error("字典配置用户默认关联角色主键列表有误。message={}", e.getMessage(), e);
            throw new CzzException("字典配置的角色主键列表有误，请务必及时处理");
        }

        // 获取用户默认头像
        String avatar = dictWrapper.getOneDict(DictEnum.DEFAULT_USER_AVATAR).getValue();

        // 解析凯撒移位加密算法得到的明文
        String password = CaesarUtils.decodePwd(registerVo.getPassword());

        // 生成盐
        String salt = RandomNumUtils.getRandomSizeNum(20);

        /*
            生成一个用户编号
         */
        Long userNo = userSequenceService.get();

        /*
            保存用户
         */
        UserEntity user = new UserEntity();
        user.setUsername(registerVo.getUsername())
                .setUserNo(userHelper.fullUserNo(userNo))
                .setAvatar(avatar)
                .setPhone(registerVo.getPhone())
                .setEmail(registerVo.getEmail())
                .setLoginName(StringUtils.EMPTY)
                .setPassword(ShaUtils.encodeSha256(password, salt))
                .setSalt(salt)
                .setStatus(CommonEnum.ZERO.getValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser("自主注册")
                .setCreateUserId(CommonEnum.ZERO.getLongValue());
        userService.saveOrEdit(user);

        /*
            保存用户关联部门
         */
        DeptUserEntity deptUser = new DeptUserEntity();
        deptUser.setUserId(user.getUserId())
                .setDeptId(deptId)
                .setMasterStatus(CommonEnum.ZERO.getValue())
                .setLeaderStatus(CommonEnum.ONE.getValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser("自主注册")
                .setCreateUserId(CommonEnum.ZERO.getLongValue());
        deptUserService.saveOrEdit(deptUser);

        /*
            保存用户关联角色
         */
        if (!roleIdList.isEmpty()) {
            List<UserRoleEntity> saveList = new ArrayList<>();
            for (Long roleId : roleIdList) {
                UserRoleEntity userRole = new UserRoleEntity();
                userRole.setUserId(user.getUserId())
                        .setRoleId(roleId)
                        .setRelationMode(RelationModeEnum.DEPT_ROLE.getValue())
                        .setDeleteStatus(CommonEnum.ZERO.getValue())
                        .setUpdateTime(date)
                        .setUpdateUser(StringUtils.EMPTY)
                        .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                        .setCreateTime(date)
                        .setCreateUser(user.getUsername())
                        .setCreateUserId(user.getUserId());
                saveList.add(userRole);
            }
            userRoleService.batchSave(saveList);
        }

        /*
            用户信息注册完成后，实现用户自动登录功能
         */
        RequestChannelEnum requestChannelEnum = registerVo.getRequestChannelEnum();
        Integer channel = requestChannelEnum.getValue();

        /*
                生成用户令牌
             */
        // 获取令牌过期时间
        Long accessTokenExpire = accessTokenProperty.getAccessTokenExpire(requestChannelEnum);
        long fAccessTokenExpire = now + accessTokenExpire;
        // 获取刷新令牌过期时间
        Long refreshTokenExpire = accessTokenProperty.getRefreshTokenExpire(requestChannelEnum);
        long fRefreshTokenExpire = now + refreshTokenExpire;

        // 用户id类型转化
        String sUserId = user.getUserId().toString();

        // 生成访问令牌
        String accessToken = aesService.czzEncrypt(sUserId);
        // 生成刷新令牌
        String refreshToken = aesService.czzEncrypt(sUserId);

        // 获取用户请求的现实地址
        IpAddressEntity ipAddress = ipAddressService.getIpAddress(loginIP);
        String requestAddress = ipAddress.getRequestAddress();

        /*
            保存用户登录记录
         */
        UserLoginEntity userLogin = new UserLoginEntity();
        userLogin.setUserId(user.getUserId())
                .setChannel(channel)
                .setType(
                        StringUtils.isBlank(registerVo.getPhone()) ?
                                LoginTypeEnum.PHONE.getValue() : LoginTypeEnum.EMAIL.getValue()
                )
                .setLoginIp(loginIP)
                .setLoginAddress(requestAddress)
                .setLoginTime(now)
                .setExpireTime(fRefreshTokenExpire)
                .setStatus(CommonEnum.ZERO.getValue())
                .setDeleteStatus(CommonEnum.ZERO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                .setCreateTime(date)
                .setCreateUser(user.getUsername())
                .setCreateUserId(user.getUserId());
        userLoginService.saveOrEdit(userLogin);

        /*
            保存用户令牌
         */
        UserAccessTokenEntity saveUserAccessToken = new UserAccessTokenEntity();
        saveUserAccessToken.setUserId(user.getUserId())
                .setChannel(channel)
                .setAccessToken(accessToken)
                .setAccessExpireTime(fAccessTokenExpire)
                .setRefreshToken(refreshToken)
                .setRefreshExpireTime(fRefreshTokenExpire);
        userAccessTokenService.saveOrEdit(saveUserAccessToken);

        // 异步操作
        asyncService.execute(() -> {
            /*
                保存部门与用户关联流水
             */
            DeptUserFlowEntity userDeptFlow = new DeptUserFlowEntity();
            userDeptFlow.setUserId(user.getUserId())
                    .setPreDeptId(CommonEnum.ZERO.getLongValue())
                    .setNextDeptId(deptId)
                    .setMoveMode(CommonEnum.MINUS_ONE.getValue())
                    .setMoveRemark(StringUtils.EMPTY)
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(user.getUsername())
                    .setCreateUserId(user.getUserId());
            deptUserService.saveOrEdit(deptUser);

            /*
                保存用户登录记录
             */
            UserLoginFlowEntity userLoginFlow = new UserLoginFlowEntity();
            userLoginFlow.setUserId(user.getUserId())
                    .setChannel(channel)
                    .setType(userLogin.getType())
                    .setLoginIp(loginIP)
                    .setLoginAddress(requestAddress)
                    .setDeleteStatus(CommonEnum.ZERO.getValue())
                    .setLogoutTime(CommonEnum.ZERO.getLongValue())
                    .setUpdateTime(date)
                    .setUpdateUser(StringUtils.EMPTY)
                    .setUpdateUserId(CommonEnum.ZERO.getLongValue())
                    .setCreateTime(date)
                    .setCreateUser(user.getUsername())
                    .setCreateUserId(user.getUserId());
            userLoginFlowService.saveOrEdit(userLoginFlow);

            List<UserContactFlowEntity> userContactFlowList = new ArrayList<>();
            if (StringUtils.isNotBlank(registerVo.getPhone())) {
                // 保存用户手机号记录
                UserContactFlowEntity userContactFlow = new UserContactFlowEntity();
                userContactFlow.setUserId(user.getUserId())
                        .setType(UserContactFlowEnum.PHONE.getValue())
                        .setMode(UserContactFlowEnum.BIND.getValue())
                        .setContact(registerVo.getPhone())
                        .setDeleteFlag(CommonEnum.ZERO.getValue())
                        .setDeleteTime(date)
                        .setCreateTime(date)
                        .setCreateUser(user.getUserId());
                userContactFlowList.add(userContactFlow);
            }
            if (StringUtils.isNotBlank(registerVo.getEmail())) {
                // 保存用户邮箱记录
                UserContactFlowEntity userContactFlow = new UserContactFlowEntity();
                userContactFlow.setUserId(user.getUserId())
                        .setType(UserContactFlowEnum.EMAIL.getValue())
                        .setMode(UserContactFlowEnum.BIND.getValue())
                        .setContact(registerVo.getEmail())
                        .setDeleteFlag(CommonEnum.ZERO.getValue())
                        .setDeleteTime(date)
                        .setCreateTime(date)
                        .setCreateUser(user.getUserId());
                userContactFlowList.add(userContactFlow);
            }
            if (!userContactFlowList.isEmpty()) {
                userContactFlowService.batchSave(userContactFlowList);
            }

            /*
                更新用户关联序号
             */
            userSequenceService.update(userNo, user.getUserId());
        });

        // 向Redis中保存用户登录失效时间信息
//        redisZSetHelper.add(RedisConstant.OVER_TIME_ZSET, userId, overTime);

        LoginDto loginDto = new LoginDto();
        loginDto.setUsername(user.getUsername())
                .setAvatar(user.getAvatar())
                .setToken(accessToken)
                .setExpireTime(fAccessTokenExpire)
                .setAccessToken(accessToken)
                .setAccessTokenExpire(fAccessTokenExpire)
                .setRefreshToken(refreshToken)
                .setRefreshTokenExpire(fRefreshTokenExpire);
        return loginDto;
    }

}
